package impl

import (
	"adam2/internal/constants"
	domain "adam2/internal/domain"
	"adam2/internal/dto"
	"adam2/internal/model"
	"adam2/internal/properties"
	"adam2/internal/vo"
	pConstants "anubis-framework/pkg/constants"
	pDomain "anubis-framework/pkg/domain"
	"anubis-framework/pkg/util"
	"math/rand"
	"time"
)

type TrainingInfoServiceImpl struct {
	*BaseServiceImpl
}

// 获取service的实现类
func GetTrainingInfoServiceImpl() *TrainingInfoServiceImpl {
	return &TrainingInfoServiceImpl{GetBaseServiceImpl()}
}

// 添加
func (_trainingInfoServiceImpl TrainingInfoServiceImpl) Add(addTrainingInfoVo *vo.AddTrainingInfoVo) *pDomain.ServiceResult {
	// 类型转换
	var trainingInfo model.TrainingInfo = model.TrainingInfo{}
	trainingInfo.Name = addTrainingInfoVo.Name
	if addTrainingInfoVo.Market == 0 {
		// 2表示ETF，现在ETF并不参与训练，因此现在只能按照下面的做法来解决
		var marketArray = []int{1, 3}
		trainingInfo.Market = marketArray[rand.Intn(len(marketArray))]
	} else {
		trainingInfo.Market = addTrainingInfoVo.Market
	}
	var code string
	if addTrainingInfoVo.Code == "" {
		if trainingInfo.Market == constants.STOCK_MARKET {
			code = _trainingInfoServiceImpl._stockTransactionDataAllDaoImpl.FindRandomStockCode(addTrainingInfoVo.BeginDate, addTrainingInfoVo.EndDate)
			if addTrainingInfoVo.BeginDate == "" || addTrainingInfoVo.EndDate == "" {
				var maxDataAndMinData domain.MaxDataAndMinData = _trainingInfoServiceImpl._stockTransactionDataAllDaoImpl.FindMinDateAndMaxDate(code)
				trainingInfo.BeginDate = maxDataAndMinData.MinDate.Add(time.Hour * 24 * time.Duration(properties.TrainingProperties_.DateInterval))
				trainingInfo.EndDate = trainingInfo.BeginDate.Add(time.Hour * 24 * time.Duration(properties.TrainingProperties_.DateInterval))
				// 有时候trainingInfo.EndDate正好不是交易日，因此此处还需要再此查询数据库
				trainingInfo.EndDate = _trainingInfoServiceImpl._stockTransactionDataAllDaoImpl.FindBeforeOneByStockCodeAndDate(code, util.DateToString(trainingInfo.EndDate))
			} else {
				trainingInfo.BeginDate = util.StringToDate(addTrainingInfoVo.BeginDate)
				trainingInfo.EndDate = util.StringToDate(addTrainingInfoVo.EndDate)
			}
		}
		if trainingInfo.Market == constants.COMMODITY_FUTURE_MARKET {
			code = _trainingInfoServiceImpl._commodityFutureDateDataDaoImpl.FindRandomCommodityFutureCode(addTrainingInfoVo.BeginDate, addTrainingInfoVo.EndDate)
			if addTrainingInfoVo.BeginDate == "" || addTrainingInfoVo.EndDate == "" {
				var maxDataAndMinData domain.MaxDataAndMinData = _trainingInfoServiceImpl._commodityFutureDateDataDaoImpl.FindMinDateAndMaxDate(code)
				trainingInfo.BeginDate = maxDataAndMinData.MinDate.Add(time.Hour * 24 * time.Duration(properties.TrainingProperties_.DateInterval))
				trainingInfo.EndDate = trainingInfo.BeginDate.Add(time.Hour * 24 * time.Duration(properties.TrainingProperties_.DateInterval))
				// 有时候trainingInfo.EndDate正好不是交易日，因此此处还需要再此查询数据库
				trainingInfo.EndDate = _trainingInfoServiceImpl._commodityFutureDateDataDaoImpl.FindBeforeOneByCodeAndTransactionDate(code, util.DateToString(trainingInfo.EndDate))
			} else {
				trainingInfo.BeginDate = util.StringToDate(addTrainingInfoVo.BeginDate)
				trainingInfo.EndDate = util.StringToDate(addTrainingInfoVo.EndDate)
			}
		}
		var encryptCode, _ = util.Encrypt(code, properties.TrainingProperties_.EncryptKey, properties.TrainingProperties_.IV)
		trainingInfo.Code = encryptCode
	} else {
		if trainingInfo.Market == constants.STOCK_MARKET {
			if addTrainingInfoVo.BeginDate == "" || addTrainingInfoVo.EndDate == "" {
				var maxDataAndMinData domain.MaxDataAndMinData = _trainingInfoServiceImpl._stockTransactionDataAllDaoImpl.FindMinDateAndMaxDate(addTrainingInfoVo.Code)
				trainingInfo.BeginDate = maxDataAndMinData.MinDate.Add(time.Hour * 24 * time.Duration(properties.TrainingProperties_.DateInterval))
				trainingInfo.EndDate = trainingInfo.BeginDate.Add(time.Hour * 24 * time.Duration(properties.TrainingProperties_.DateInterval))
				// 有时候trainingInfo.EndDate正好不是交易日，因此此处还需要再此查询数据库
				trainingInfo.EndDate = _trainingInfoServiceImpl._stockTransactionDataAllDaoImpl.FindBeforeOneByStockCodeAndDate(code, util.DateToString(trainingInfo.EndDate))
			} else {
				trainingInfo.BeginDate = util.StringToDate(addTrainingInfoVo.BeginDate)
				trainingInfo.EndDate = util.StringToDate(addTrainingInfoVo.EndDate)
			}
		}
		if trainingInfo.Market == constants.COMMODITY_FUTURE_MARKET {
			if addTrainingInfoVo.BeginDate == "" || addTrainingInfoVo.EndDate == "" {
				var maxDataAndMinData domain.MaxDataAndMinData = _trainingInfoServiceImpl._commodityFutureDateDataDaoImpl.FindMinDateAndMaxDate(addTrainingInfoVo.Code)
				trainingInfo.BeginDate = maxDataAndMinData.MinDate.Add(time.Hour * 24 * time.Duration(properties.TrainingProperties_.DateInterval))
				trainingInfo.EndDate = trainingInfo.BeginDate.Add(time.Hour * 24 * time.Duration(properties.TrainingProperties_.DateInterval))
				// 有时候trainingInfo.EndDate正好不是交易日，因此此处还需要再此查询数据库
				trainingInfo.EndDate = _trainingInfoServiceImpl._commodityFutureDateDataDaoImpl.FindBeforeOneByCodeAndTransactionDate(code, util.DateToString(trainingInfo.EndDate))
			} else {
				trainingInfo.BeginDate = util.StringToDate(addTrainingInfoVo.BeginDate)
				trainingInfo.EndDate = util.StringToDate(addTrainingInfoVo.EndDate)
			}
		}
		var encryptCode, _ = util.Encrypt(addTrainingInfoVo.Code, properties.TrainingProperties_.EncryptKey, properties.TrainingProperties_.IV)
		trainingInfo.Code = encryptCode
	}
	trainingInfo.CreateTime = time.Now()
	trainingInfo.Status = constants.TRAINING_WAITING

	// 保存
	var serviceResult *pDomain.ServiceResult = &pDomain.ServiceResult{}
	err := _trainingInfoServiceImpl._trainingInfoDaoImpl.Add(&trainingInfo)
	if err == nil {
		serviceResult.Code = pConstants.OPERATION_SUCCESS_CODE
		serviceResult.Message = pConstants.OPERATION_SUCCESS_MESSAGE
		serviceResult.Success = true
		return serviceResult
	} else {
		serviceResult.Code = pConstants.OPERATION_FAIL_CODE
		serviceResult.Message = pConstants.OPERATION_FAIL_MESSAGE
		serviceResult.Success = false
		return serviceResult
	}
}

// 根据name判断记录是否已经存在
func (_trainingInfoServiceImpl TrainingInfoServiceImpl) ExistByName(name string) *pDomain.ServiceResult {
	var trainingInfo model.TrainingInfo = _trainingInfoServiceImpl._trainingInfoDaoImpl.FindByName(name)
	var serviceResult *pDomain.ServiceResult = &pDomain.ServiceResult{}
	if trainingInfo.ID != 0 {
		serviceResult.Code = pConstants.OPERATION_FAIL_CODE
		serviceResult.Message = "参数name重复"
		serviceResult.Success = false
		return serviceResult
	} else {
		serviceResult.Code = pConstants.OPERATION_SUCCESS_CODE
		serviceResult.Message = pConstants.OPERATION_SUCCESS_MESSAGE
		serviceResult.Success = true
		serviceResult.Result = trainingInfo
		return serviceResult
	}
}

// 根据name查询
func (_trainingInfoServiceImpl TrainingInfoServiceImpl) FindByName(name string) *pDomain.ServiceResult {
	var trainingInfo model.TrainingInfo = _trainingInfoServiceImpl._trainingInfoDaoImpl.FindByName(name)
	var serviceResult *pDomain.ServiceResult = &pDomain.ServiceResult{}
	serviceResult.Code = pConstants.OPERATION_SUCCESS_CODE
	serviceResult.Message = pConstants.OPERATION_SUCCESS_MESSAGE
	serviceResult.Success = true
	serviceResult.Result = trainingInfo
	return serviceResult
}

// 分页显示
func (_trainingInfoServiceImpl TrainingInfoServiceImpl) Page(name string, pageNo int, pageSize int) *pDomain.ServiceResult {
	var trainingInfoDtoArray dto.TrainingInfoDtoArray = _trainingInfoServiceImpl._trainingInfoDaoImpl.Page(name, pageNo, pageSize)
	var total int = _trainingInfoServiceImpl._trainingInfoDaoImpl.PageTotal(name)
	var pageCount int
	if total%pageSize == 0 {
		pageCount = total / pageSize
	} else {
		pageCount = total/pageSize + 1
	}
	var pageDto dto.PageDto = dto.PageDto{trainingInfoDtoArray, total, pageCount}
	var serviceResult *pDomain.ServiceResult = &pDomain.ServiceResult{}
	serviceResult.Code = pConstants.OPERATION_SUCCESS_CODE
	serviceResult.Message = pConstants.OPERATION_SUCCESS_MESSAGE
	serviceResult.Success = true
	serviceResult.Result = pageDto
	return serviceResult
}

// 根据name更新status
func (_trainingInfoServiceImpl TrainingInfoServiceImpl) UpdateStatusByName(name string, status int) *pDomain.ServiceResult {
	_trainingInfoServiceImpl._trainingInfoDaoImpl.UpdateStatusByName(name, status)
	var serviceResult *pDomain.ServiceResult = &pDomain.ServiceResult{}
	serviceResult.Code = pConstants.OPERATION_SUCCESS_CODE
	serviceResult.Message = pConstants.OPERATION_SUCCESS_MESSAGE
	serviceResult.Success = true
	return serviceResult
}

// 删除训练和对应的交易记录
func (_trainingInfoServiceImpl TrainingInfoServiceImpl) DeleteTraining(name string) *pDomain.ServiceResult {
	// 查询训练
	var trainingInfo model.TrainingInfo = _trainingInfoServiceImpl._trainingInfoDaoImpl.FindByName(name)

	// 删除训练
	_trainingInfoServiceImpl._trainingInfoDaoImpl.DeleteTraining(name)

	// 删除训练对应的交易记录
	_trainingInfoServiceImpl._trainingTransactionDataDaoImpl.DeleteTrainingTransactionDataByTrainingInfoId(trainingInfo.ID)

	var serviceResult *pDomain.ServiceResult = &pDomain.ServiceResult{}
	serviceResult.Code = pConstants.OPERATION_SUCCESS_CODE
	serviceResult.Message = pConstants.OPERATION_SUCCESS_MESSAGE
	serviceResult.Success = true
	return serviceResult
}

// 根据trainingName。返回下一个交易日的K线数据，更新trng_info表
func (_trainingInfoServiceImpl TrainingInfoServiceImpl) NextDate(name string) *pDomain.ServiceResult {
	var trainingInfo model.TrainingInfo = _trainingInfoServiceImpl._trainingInfoDaoImpl.FindByName(name)
	code, _ := util.Decrypt(trainingInfo.Code, properties.TrainingProperties_.EncryptKey, properties.TrainingProperties_.IV)
	var oldEndDate string = util.DateToString(trainingInfo.EndDate)

	var serviceResult *pDomain.ServiceResult = &pDomain.ServiceResult{}
	serviceResult.Code = pConstants.OPERATION_SUCCESS_CODE
	serviceResult.Message = pConstants.OPERATION_SUCCESS_MESSAGE
	serviceResult.Success = true

	// 股票
	if trainingInfo.Market == constants.STOCK_MARKET {
		var stockTransactionDataAll model.StockTransactionDataAll = _trainingInfoServiceImpl._stockTransactionDataAllDaoImpl.FindNextByCodeAndDate(code, oldEndDate)

		if stockTransactionDataAll.ID == 0 {
			serviceResult.Code = pConstants.NOT_EXISTS_CODE
			serviceResult.Message = "已经是最后一条记录了"
			serviceResult.Success = false
			return serviceResult
		}

		var newBeginDate string = util.BeforeNDay(util.DateToString(stockTransactionDataAll.Date), properties.TrainingProperties_.DateInterval)
		var stockTransactionDataAllArray model.StockTransactionDataAllArray = _trainingInfoServiceImpl._stockTransactionDataAllDaoImpl.FindByCodeBetweenDateOrderByDateAsc(stockTransactionDataAll.Code, newBeginDate, util.DateToString(stockTransactionDataAll.Date))

		// 数据转换
		var kLineData domain.KLineData = domain.KLineData{}
		kLineData.Code = stockTransactionDataAllArray[0].Code
		kLineData.Name = stockTransactionDataAllArray[0].Code
		//kLineData.Lines = [][6]interface{}
		for _, _stockTransactionDataAll := range stockTransactionDataAllArray {
			var date_ string = util.DateToString(_stockTransactionDataAll.Date)
			var row []interface{}
			row = append(row, date_, _stockTransactionDataAll.OpenPrice, _stockTransactionDataAll.ClosePrice,
				_stockTransactionDataAll.LowestPrice, _stockTransactionDataAll.HighestPrice, _stockTransactionDataAll.Ma5,
				_stockTransactionDataAll.Ma10, _stockTransactionDataAll.Ma20, _stockTransactionDataAll.Ma60,
				_stockTransactionDataAll.Ma120, _stockTransactionDataAll.Ma250, _stockTransactionDataAll.Up, _stockTransactionDataAll.Mb,
				_stockTransactionDataAll.Dn, _stockTransactionDataAll.Ema12, _stockTransactionDataAll.Ema26,
				_stockTransactionDataAll.Dif, _stockTransactionDataAll.Dea, _stockTransactionDataAll.Macd,
				_stockTransactionDataAll.K, _stockTransactionDataAll.D)
			kLineData.Lines = append(kLineData.Lines, row)
		}
		serviceResult.Result = kLineData

		// 更新trng_info表
		_trainingInfoServiceImpl._trainingInfoDaoImpl.UpdateBeginDateAndEndDateByName(trainingInfo.Name, newBeginDate, util.DateToString(stockTransactionDataAll.Date))
	}
	// 期货
	if trainingInfo.Market == constants.COMMODITY_FUTURE_MARKET {
		var commodityFutureDateData model.CommodityFutureDateData = _trainingInfoServiceImpl._commodityFutureDateDataDaoImpl.FindNextByCodeAndDate(code, oldEndDate)

		if commodityFutureDateData.ID == 0 {
			serviceResult.Code = pConstants.NOT_EXISTS_CODE
			serviceResult.Message = "已经是最后一条记录了"
			serviceResult.Success = false
			return serviceResult
		}

		var newBeginDate string = util.BeforeNDay(util.DateToString(commodityFutureDateData.TransactionDate), properties.TrainingProperties_.DateInterval)
		var commodityFutureDateDataArray model.CommodityFutureDateDataArray = _trainingInfoServiceImpl._commodityFutureDateDataDaoImpl.FindByCodeBetweenDateOrderByDateAsc(commodityFutureDateData.Code, newBeginDate, util.DateToString(commodityFutureDateData.TransactionDate))

		// 数据转换
		var kLineData domain.KLineData = domain.KLineData{}
		kLineData.Code = commodityFutureDateDataArray[0].Code
		kLineData.Name = commodityFutureDateDataArray[0].Code
		//kLineData.Lines = [][6]float64{}
		for _, _commodityFutureDateData := range commodityFutureDateDataArray {
			var date_ string = util.DateToString(_commodityFutureDateData.TransactionDate)
			var row []interface{}
			row = append(row, date_, _commodityFutureDateData.OpenPrice, _commodityFutureDateData.ClosePrice,
				_commodityFutureDateData.LowestPrice, _commodityFutureDateData.HighestPrice, _commodityFutureDateData.Ma5,
				_commodityFutureDateData.Ma10, _commodityFutureDateData.Ma20, _commodityFutureDateData.Ma60,
				_commodityFutureDateData.Ma120, _commodityFutureDateData.Ma250, _commodityFutureDateData.Up, _commodityFutureDateData.Mb,
				_commodityFutureDateData.Dn, _commodityFutureDateData.Ema12, _commodityFutureDateData.Ema26,
				_commodityFutureDateData.Dif, _commodityFutureDateData.Dea, _commodityFutureDateData.Macd,
				_commodityFutureDateData.K, _commodityFutureDateData.D)
			kLineData.Lines = append(kLineData.Lines, row)
		}
		serviceResult.Result = kLineData

		// 更新trng_info表
		_trainingInfoServiceImpl._trainingInfoDaoImpl.UpdateBeginDateAndEndDateByName(trainingInfo.Name, newBeginDate, util.DateToString(commodityFutureDateData.TransactionDate))
	}

	return serviceResult
}
